#include "formula.h"

Formula::Formula() {
    elements = new LinkedList<FormulaElement>();
    fitness=1000000000000000000000000000.0;
    wasNeverNan=true;
}

Formula::~Formula() {
    delete elements;
}

std::string& Formula::toString() {
    if(!elements)
        return *(new std::string("null"));
    std::string *returnString = new std::string();
    elements->resetIteratorPosition();
    bool addSpace = false;
    while(elements->iteratorHasNext()) {
        if(addSpace)
            returnString->append(" ");
        else
            addSpace=true;
        std::string& elementString = elements->iteratorGetNext().toString();
        returnString->append(elementString);
        delete &elementString;
    }
    return *returnString;
}

void Formula::evaluate(InputIterator& myInputIterator) {
    if(wasNeverNan==false) {
        // shouldn't continue use Formula which gives nan or inf results
        assert(false);
        return;
    }
    assert(invariant(false));
    assert(elements->getSize()>0);
    assert(myInputIterator.wasReseted());
    fitness=0;
    LinkedList<double> *evaluationStack = new LinkedList<double>();
    while(myInputIterator.hasNext() && wasNeverNan) {
        fitness+=evaluatePosition(myInputIterator.getNext(), evaluationStack);
    }
    if(fitness)
        fitness+=elements->getSize();
    delete evaluationStack;
    assert(invariant(false));
}

const double& Formula::getFitness() {
    return fitness;
}

void Formula::mutate() {
    assert(invariant(false));
    assert(elements->getSize()>0);
	int mutateLevel=(rand()>>24);	// ~RAND_MAX=63 (0 bis u mit 63)
	int level=0;					// local level
	int chance;

	elements->resetIteratorPosition();
	while(elements->iteratorHasNext()) {  // Iteration over all the Elements in the Formula
	    assert(invariant(true));
        chance=(rand()>>26);		      // ~RAND_MAX=31 (0 bis u. mit 31)
        if(chance>mutateLevel) {          // Here we decided to mutate. Now we have to choose how we mutate:
            chance=(rand()>>25);		      // ~RAND_MAX=31 (0 bis u. mit 63)
            if(chance<7) {
                // add new element
                mutateAddNewElementAtIteratorPosition(chance, level, elements->iteratorPositionIsCloseToStart());
                assert(invariant(true));
                assert(invariantSpecialMutationLevelTest(level));
            }
            else if(chance<20) {
                // delete one
                mutateDeleteElementAtIteratorPosition(chance, level);
                assert(invariant(true));
                assert(invariantSpecialMutationLevelTest(level));
            }
            else {
                // element mutate
                mutateElementMutate();
                assert(invariant(true));
                assert(invariantSpecialMutationLevelTest(level));
            }
        }
        else {
            if(elements->iteratorHasNext())
                elements->iteratorGetNext();
                assert(invariant(true));
                assert(invariantSpecialMutationLevelTest(level));
        }
    }
    assert(invariant(true));

    // bring level to the correct value
    mutateBringLevelBackToZero(level);
    assert(invariant(false));
}

bool Formula::invariant(bool isInbetweenMutation) {
    if(!isInbetweenMutation) {
        assert((elements->getSize())%2==1);
        if(elements->getSize()!=1)
            assert(!elements->getElementAtEnd().isNumber());
    }
    int level=0;
//    if(isInbetweenMutation)
        elements->saveIteratorPositionNow();
    elements->resetIteratorPosition();
    while (elements->iteratorHasNext()) {
        if(elements->iteratorGetNext().isNumber())
            ++level;
        else {
            assert(level>1);
            --level;
        }
        assert(level>0);
    }
//    if(isInbetweenMutation)
        elements->loadOldIteratorPosition();
//    else {
        if(!isInbetweenMutation)
            assert(level==1);
//    }
    return true;
}

Formula* Formula::copy() {
    Formula* returnFormula=new Formula();
    elements->resetIteratorPosition();
    while (elements->iteratorHasNext()) {
        returnFormula->getListOfFormulaElements().addAtEnd(elements->iteratorGetNext().copy());
    }
    returnFormula->wasNeverNan=wasNeverNan;
    returnFormula->setFitness(fitness);
    return returnFormula;
}

bool operator<(Formula& f1, Formula& f2) {
    return f1.getFitness()<f2.getFitness();
}

bool operator>(Formula& f1, Formula& f2) {
    return f1.getFitness()>f2.getFitness();
}

std::string& Formula::toHumanString() {
    if(!elements)
        return *(new std::string("null"));
    LinkedList<std::string>& toStringStack=*(new LinkedList<std::string>());
    elements->resetIteratorPosition();
    while(elements->iteratorHasNext()) {
        elements->iteratorGetNext().createHumanReadeableToString(toStringStack);
    }
    assert(toStringStack.getSize()==1);
    std::string& returnString = toStringStack.pop();
    delete &toStringStack;
    return returnString;
}

bool Formula::invariantSpecialMutationLevelTest(const int& level) {
    int testLevel=0;
    elements->saveIteratorPositionNow();
    elements->resetIteratorPosition();
    while (elements->iteratorHasNext()) {
        if(elements->iteratorGetNext().isNumber())
            ++testLevel;
        else {
            assert(testLevel>1);
            --testLevel;
        }
        assert(testLevel>0);
    }
    elements->loadOldIteratorPosition();
    assert(level==(testLevel-1));
    return true;
}


void Formula::simplify() {
    assert(invariant(false));
    assert(elements->getSize()>0);
    LinkedList<FormulaElement> *simplificationStack = new LinkedList<FormulaElement>();
    LinkedList<int> *typeList = new LinkedList<int>();
    elements->resetIteratorPosition();

    while(elements->iteratorHasNext()) {
        if(elements->getElementAtIteratorPosition().isNumber()) {
                    assert(&elements->getElementAtIteratorPosition());
            std::string& s=elements->getElementAtIteratorPosition().toString();
            if (RealValueCheck::checkIfRealValueIsVariable(s)) {
                simplificationStack->addAtEnd(elements->getElementAtIteratorPosition().copy());
                typeList->addAtEnd(*(new int(-1)));
            }
            else {
                simplificationStack->addAtEnd(elements->getElementAtIteratorPosition().copy());
                typeList->addAtEnd(*(new int(0)));
            }
            delete &s;
        }
        else {
            std::string &tempString = elements->getElementAtIteratorPosition().toString();
            if(!tempString.compare("+")) {
                if(typeList->getSecondLastElement()==0 && typeList->getElementAtEnd()==0) {
                    simplifyCalculateTwoNumbers(simplificationStack, typeList);
                }
                else {
                    simplificationStack->addAtEnd(elements->getElementAtIteratorPosition().copy());
                    typeList->addAtEnd(*(new int(1)));
                }
            }
            else if(!tempString.compare("-")) {
                if(typeList->getSecondLastElement()==0 && typeList->getElementAtEnd()==0) {
                    simplifyCalculateTwoNumbers(simplificationStack, typeList);
                }
                else {
                    simplificationStack->addAtEnd(elements->getElementAtIteratorPosition().copy());
                    typeList->addAtEnd(*(new int(2)));
                }
            }
            else if(!tempString.compare("*")) {
                if(typeList->getSecondLastElement()==0 && typeList->getElementAtEnd()==0) {
                    simplifyCalculateTwoNumbers(simplificationStack, typeList);
                }
                else if(typeList->getSecondLastElement()==-1 && typeList->getElementAtEnd()==-1) {
//                    FormulaElement &fe2=simplificationStack->pop();
                    delete &simplificationStack->pop();
                    delete &typeList->pop();
                    simplificationStack->addAtEnd(*(new RealNumber(2)));
                    typeList->addAtEnd(*(new int(0)));
                    simplificationStack->addAtEnd(*(new RealPow()));
                    typeList->addAtEnd(*(new int(4)));
                }
                else {
                    simplificationStack->addAtEnd(elements->getElementAtIteratorPosition().copy());
                    typeList->addAtEnd(*(new int(3)));
                }
            }
            else if(!tempString.compare("^")) {
                if(typeList->getSecondLastElement()==0 && typeList->getElementAtEnd()==0) {
                    simplifyCalculateTwoNumbers(simplificationStack, typeList);
                }
                else {
                    simplificationStack->addAtEnd(elements->getElementAtIteratorPosition().copy());
                    typeList->addAtEnd(*(new int(4)));
                }
            }
            else if(!tempString.compare("/")) {
                if(typeList->getSecondLastElement()==0 && typeList->getElementAtEnd()==0) {
                    simplifyCalculateTwoNumbers(simplificationStack, typeList);
                }
                else {
                    simplificationStack->addAtEnd(elements->getElementAtIteratorPosition().copy());
                    typeList->addAtEnd(*(new int(5)));
                }
            }
            delete &tempString;
        }
        elements->iteratorGetNext();
    }

//    std::string &temp=toHumanString();
//    std::cout<<temp<<std::endl;
//    delete &temp;
    LinkedList<FormulaElement> *deleteMe=elements;
    elements=simplificationStack;
//    std::string &temp2=toHumanString();
//    std::cout<<temp2<<std::endl;
//    delete &temp2;

    assert(invariant(false));

    delete typeList;
    delete deleteMe;
    assert(invariant(false));
}

void Formula::simplifyCalculateTwoNumbers(LinkedList<FormulaElement> *simplificationStack, LinkedList<int> *typeList) {
        assert(invariant(false));
        assert(&(elements->getElementAtIteratorPosition()));
    LinkedList<double> *evaluationStack = new LinkedList<double>;
    FormulaElement &fe2=simplificationStack->pop();
    FormulaElement &fe1=simplificationStack->pop();
    fe1.evaluateMe(*evaluationStack, 0);
    fe2.evaluateMe(*evaluationStack, 0);
            assert(&elements->getElementAtIteratorPosition());
    delete &fe1;
    delete &fe2;
    delete &(typeList->pop());
    delete &(typeList->pop());
    elements->getElementAtIteratorPosition().evaluateMe(*evaluationStack,0);
    simplificationStack->addAtEnd(*(new RealNumber(evaluationStack->getElementAtEnd())));
    typeList->addAtEnd(*(new int(0)));
    delete evaluationStack;
        assert(invariant(false));
        assert(&elements->getElementAtIteratorPosition());
}

int& Formula::getElementNumberOfLastSimplificationStackElement(LinkedList<FormulaElement> *simplificationStack) {
    int& r=*(new int());
    std::string& tempString = simplificationStack->getElementAtEnd().toString();
    if(simplificationStack->getElementAtEnd().isNumber()) {
        if(RealValueCheck::checkIfRealValueIsVariable(tempString)) {
            r=-1;
        }
        else {
            r=0;
        }
    }
    else {
        if(!tempString.compare("+"))
            r=1;
        if(!tempString.compare("-"))
            r=2;
        if(!tempString.compare("*"))
            r=3;
        if(!tempString.compare("^"))
            r=4;
        if(!tempString.compare("/"))
            r=5;
    }
    delete &tempString;
    return r;
}
